using System;
using System.Collections.Generic;
using System.Linq;
using Tools.Domain.Specifications;
using Tools.Domain.Specifications.Constraints;
using Web.App.Infrastructure.ApplicationMessages.Builder;
using Web.App.Infrastructure.ApplicationMessages.Implementation;
using Web.App.Infrastructure.Security.Contracts;
using Webshop20.Domain.Common;
using Webshop20.Domain.Common.Exceptions;
using Webshop20.Helper;

namespace Webshop20.Domain.Security.Authentication.Core
{
    public enum AccountState
    {
        inactive,
        active
    }


    [Serializable]
    public class Account : Entity
    {
        public virtual DateTime RegistrationDate { get; private set; }
        public virtual AccountState State { get; private set; }
        public virtual string Password { get; private set; }
        public virtual string PasswordConfirm { get; private set; }
        public virtual ControllQuestion ControlQuestion { get; private set; }
        public virtual string ControlAnswer { get; private set; }
        public virtual string LoginName { get; private set; }

        public Account(
            string loginName,
            string password,
            string passwordConfirm,
            ControllQuestion controlQuestion,
            string controlAnswer)  
        {
            LoginName = loginName;
            Password = password;
            PasswordConfirm = passwordConfirm;
            ControlQuestion = controlQuestion;
            ControlAnswer = controlAnswer;
        }

        public Account()
        {
            
        }




        public virtual void RegisteredAt(DateTime date)
        {
            if(date == null)
                throw new ArgumentNullException("date");

            RegistrationDate = date;
        }

        public virtual void ChangeStateTo(AccountState accountState)
        {
            State = accountState;
        }



        public virtual void SetAccountData(
            string loginName,
            string password, 
            string passwordConfirm, 
            ControllQuestion controllQuestion, 
            string controllAnswer, 
            IHashService hashService,
            IConstraintSet<Account> accountConstraintSet
            )
        {
            
            accountConstraintSet
                .ForMethod(account => account.SetAccountData(null, null, null, null, null, null, null))
                .PreconditionsAreSatisfiedBy(new SetAccountDataDTO(password, passwordConfirm));

            var errors = accountConstraintSet
                .ForMethod(account => account.SetAccountData(null, null, null, null, null, null, null)).
                GetPreconditionSpecificationErrors<SetAccountDataDTO>();

            LoginName = loginName;
            Password = hashService.BuildHash(password);
            ControlQuestion = controllQuestion;
            ControlAnswer = controllAnswer;
            
            accountConstraintSet.InvarantsAreSatisfiedBy(this);
            errors = errors.Union(accountConstraintSet.GetInvariantsSpecificationErrors());

            if(HasConstraintErrors(errors))
            {
                throw new DomainException(Mappings.MapSpecificationErrorsToRuleFailures(accountConstraintSet.GetInvariantsSpecificationErrors(),
                                                                       "Account", "SetAccountData",
                                                                       "ACCOUNT_VALIDATION_FAILED"));
            }
        }

        private bool HasConstraintErrors(IEnumerable<ISpecificationError> errors)
        {
            return errors.Count() > 0;
        }

        public virtual void ChangePassword(string oldPassword, string newPassword, string newPasswordConfirmation, IConstraintSet<Account> accountConstraintSet)
        {
            //Precondition
            if (IsNotCorrect(oldPassword))
            {
                Failure failure = new BuildFailure()
                    .WithFailureProperty("Password")
                    .WithAttemptedValue(oldPassword)
                    .WithFailureCode("PASSWORD_WRONG");

                FailureMessage failureMessage = new BuildFailureMessage()
                    .WithContext("Account")
                    .WithAction("ChangePassword").WithFailure(failure);
                throw new DomainException(failureMessage);
            }

            Password = newPassword;
            PasswordConfirm = newPasswordConfirmation;

            //Invariants
            var validationSuccessful = accountConstraintSet.InvarantsAreSatisfiedBy(this);
            if (!validationSuccessful)
            {
                throw new DomainException(Mappings.MapSpecificationErrorsToRuleFailures(accountConstraintSet.GetInvariantsSpecificationErrors(),
                                                                  "Account", "ChangePassword", "INPUT_DATA_WRONG"));
                
            }
        }

        private bool IsNotCorrect(string password)
        {
            return password != Password;
        }

        public virtual void ResetPassword()
        {
            throw new NotImplementedException();
        }


    }
}